#include "led.h"
#include "work.h"
#include "declares.h"

#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/statvfs.h>

typedef enum
{
    E_LED_RED,
    E_LED_YELLOW
} led_e;

typedef struct
{
    struct timespec on;
    struct timespec off;
} led_conf;

typedef void (*f_ptr)();

int led_cancel_req = 0;

pthread_t led_th;

static const char *yellow_trigger = "/sys/class/leds/led0/trigger";
static const char *yellow_cntl = "/sys/class/leds/led0/brightness";
static const char *red_trigger = "/sys/class/leds/led1/trigger";
static const char *red_cntl = "/sys/class/leds/led1/brightness";

static int led_fd = -1;
static led_e which = E_LED_YELLOW;
static f_ptr update_conf;
static led_conf config;

#define LED_ON(l) ((l) == E_LED_RED ? "1" : "0")
#define LED_OFF(l) ((l) == E_LED_RED ? "0" : "1")
#define ELSE(l) (((l) + 1) & 1)

#define MS 1000000

void _red_conf()
{
    if (!(started && is_running))
    {
        config.on.tv_sec = 0;
        config.on.tv_nsec = 100 * MS;
        config.off.tv_sec = 10;
        config.off.tv_nsec = 0;
        return;
    }
    config.on.tv_sec = 0;
    config.on.tv_nsec = 100 * MS;
    config.off = config.on;
}

void _yellow_conf()
{
    static struct statvfs buf;
    static const unsigned int MB = 1024 * 1024;
    if (!(started && is_running))
    {
        config.on.tv_sec = 0;
        config.on.tv_nsec = 100 * MS;
        config.off.tv_sec = 10;
        config.off.tv_nsec = 0;
        return;
    }
    statvfs("/", &buf);
    double available = (double)(buf.f_bfree) * (double)(buf.f_bsize) / MB;
    // DEBUG("available size is %lf MB", available);
    if (available < 200.0)
    {
        // DEBUG("200");
        config.on.tv_sec = 1;
        config.on.tv_nsec = 0;
        config.off.tv_sec = 0;
        config.off.tv_nsec = 0;
    }
    else if (available < 1000.0)
    {
        // DEBUG("1000");
        config.on.tv_sec = 0;
        config.on.tv_nsec = 100 * MS;
        config.off = config.on;
    }
    else
    {
        // DEBUG(">1000");
        config.on.tv_sec = 1;
        config.on.tv_nsec = 0;
        config.off = config.on;
    }
}

void clear_trigger(const char *trigger)
{
    int fd = open(trigger, O_TRUNC | O_RDWR);
    write(fd, "none", 4);
    close(fd);
}

void off(const char *cntl)
{
    if (cntl == NULL)
    {
        lseek(led_fd, 0, SEEK_SET);
        write(led_fd, LED_OFF(which), 1);
        // DEBUG("OFF: %s", LED_OFF(which));
    }
    else
    {
        int tmp = open(cntl, O_TRUNC | O_RDWR);
        lseek(tmp, 0, SEEK_SET);
        write(tmp, LED_OFF(ELSE(which)), 1);
        close(tmp);
    }
}

void on()
{
    lseek(led_fd, 0, SEEK_SET);
    write(led_fd, LED_ON(which), 1);
    // DEBUG("ON: %s", LED_ON(which));
}

void led_prepare()
{
    int tmp = -1;
    clear_trigger(red_trigger);
    clear_trigger(yellow_trigger);
    if (which == E_LED_YELLOW)
    {
        update_conf = _yellow_conf;
        off(red_cntl);
        led_fd = open(yellow_cntl, O_TRUNC | O_RDWR);
    }
    else
    {
        update_conf = _red_conf;
        off(yellow_cntl);
        led_fd = open(red_cntl, O_TRUNC | O_RDWR);
    }
}

#define led_close() \
    close(led_fd);

int blingbling(void *arg)
{
    sigset_t set;
    sigfillset(&set);
    sigdelset(&set, SIGUSR1);
    pthread_sigmask(SIG_SETMASK, &set, NULL);
    if (strcmp(mode, "log") == 0)
    {
        which = E_LED_YELLOW;
    }
    else
    {
        which = E_LED_RED;
    }
    led_prepare();
    while (1)
    {
        update_conf();
        on();
        nanosleep(&config.on, NULL);
        if (led_cancel_req)
        {
            break;
        }
        update_conf();
        off(NULL);
        nanosleep(&config.off, NULL);
        if (led_cancel_req)
        {
            break;
        }
    }
    led_close();
    return 0;
}